<!-- directive:title wangy::ES6-Promise用法讲解 -->
<!-- directive:breadcrumb ES6-Promise用法讲解 -->
<div class='panel-body'>
    <article class="docs-content">
        <p>Promise的构造函数接收一个参数，是函数，并且传入两个参数：resolve，reject，分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。其实这里用“成功”和“失败”来描述并不准确，按照标准来讲，resolve是将Promise的状态置为fullfiled，reject是将Promise的状态置为rejected。不过在我们开始阶段可以先这么理解，后面再细究概念。</p>
        <script type="text/xianjs" ui-bs>
            function runAsync(){
                return new Promise(function(resolve, reject){
                    //做一些异步操作
                    setTimeout(function(){ console.log('执行完成'); resolve('随便什么数据'); }, 2000);
                });
            }
            runAsync()
        </script>
        <p>运行代码，会在2秒后输出“执行完成”。注意！我只是new了一个对象，并没有调用它，我们传进去的函数就已经执行了，这是需要注意的一个细节。所以我们用Promise的时候一般是包在一个函数中，在需要的时候去运行这个函数</p>
        <p>在我们包装好的函数最后，会return出Promise对象，也就是说，执行这个函数我们得到了一个Promise对象。还记得Promise对象上有then、catch方法吧？这就是强大之处了</p>
        <script type="text/xianjs" ui-bs>
            runAsync().then(function(data){
                //后面可以用传过来的数据做些其他操作
            });
        </script>
        <p>在runAsync()的返回上直接调用then方法，then接收一个参数，是函数，并且会拿到我们在runAsync中调用resolve时传的的参数。</p>
        <p>你可能会不屑一顾，那么牛逼轰轰的Promise就这点能耐？我把回调函数封装一下，给runAsync传进去不也一样吗，就像这样：</p>
        <script type="text/xianjs" ui-bs>
            function runAsync(callback){ setTimeout(function(){ console.log('执行完成'); callback('随便什么数据'); }, 2000); }
            runAsync(function(data){ console.log(data); });
        </script>
        <p>效果也是一样的，还费劲用Promise干嘛。那么问题来了，有多层回调该怎么办？如果callback也是一个异步操作，而且执行完后也需要有相应的回调函数，该怎么办呢？总不能再定义一个callback2，然后给callback传进去吧。而Promise的优势在于，可以在then方法中继续写Promise对象并返回，然后继续调用then来进行回调操作。</p>
        <p>从表面上看，Promise只是能够简化层层回调的写法，而实质上，Promise的精髓是“状态”，用维护状态、传递状态的方式来使得回调函数能够及时调用，它比传递callback函数要简单、灵活的多</p>
        <script type="text/xianjs" ui-bs>
            runAsync1()
                .then(function(data){ console.log(data); return runAsync2(); })
                .then(function(data){ console.log(data); return runAsync3(); })
                .then(function(data){ console.log(data); });
        </script>
        <p>这样能够按顺序，每隔两秒输出每个异步回调中的内容，在runAsync2中传给resolve的数据，能在接下来的then方法中拿到。</p>
        <p>在then方法中，你也可以直接return数据而不是Promise对象，在后面的then中就可以接收到数据了</p>
        <hr />
        <p>到这里，你应该对“Promise是什么玩意”有了最基本的了解。那么我们接着来看看ES6的Promise还有哪些功能。我们光用了resolve，还没用reject呢，它是做什么的呢？事实上，我们前面的例子都是只有“执行成功”的回调，还没有“失败”的情况，reject的作用就是把Promise的状态置为rejected，这样我们在then中就能捕捉到，然后执行“失败”情况的回调。</p>
        <script type="text/xianjs" ui-bs>
            function getNumber(){
                return new Promise(function(resolve, reject){
                    setTimeout(function(){
                        var num = Math.ceil(Math.random()*10);
                        if(num<=5){ resolve(num); } else{ reject('数字太大了'); }
                    }, 2000);
                });
            }
            getNumber()
            .then(
                function(data){ console.log('resolved'); console.log(data); },
                function(reason, data){ console.log('rejected'); console.log(reason); }
            );
        </script>
        <p>getNumber函数用来异步获取一个数字，2秒后执行完成，如果数字小于等于5，我们认为是“成功”了，调用resolve修改Promise的状态。否则我们认为是“失败”了，调用reject并传递一个参数，作为失败的原因。</p>
        <p>我们知道Promise对象除了then方法，还有一个catch方法，它是做什么用的呢？其实它和then的第二个参数一样，用来指定reject的回调</p>
        <script type="text/xianjs" ui-bs>
            getNumber()
                .then(function(data){ console.log('resolved'); console.log(data); })
                .catch(function(reason){ console.log('rejected'); console.log(reason); });
        </script>
        <p>效果和写在then的第二个参数里面一样。不过它还有另外一个作用：在执行resolve的回调（也就是上面then中的第一个参数）时，如果抛出异常了（代码出错了），那么并不会报错卡死js，而是会进到这个catch方法中。</p>
        <p>进到catch方法里面去了，而且把错误原因传到了reason参数中。即便是有错误的代码也不会报错了，这与我们的try/catch语句有相同的功能。</p>
        <hr>
        <p>Promise的all方法提供了并行执行异步操作的能力，并且在所有异步操作执行完后才执行回调。我们仍旧使用上面定义好的runAsync1、runAsync2、runAsync3这三个函数</p>
        <script type="text/xianjs" ui-bs>
            Promise
            .all([runAsync1(), runAsync2(), runAsync3()])
            .then(function(results){ console.log(results); });
        </script>
        <p>用Promise.all来执行，all接收一个数组参数，里面的值最终都算返回Promise对象。这样，三个异步操作的并行执行的，等到它们都执行完后才会进到then里面。那么，三个异步操作返回的数据哪里去了呢？都在then里面呢，all会把所有异步操作的结果放进一个数组中传给then，就是上面的results。</p>
        <p>有了all，你就可以并行执行多个异步操作，并且在一个回调中处理所有的返回数据，是不是很酷？有一个场景是很适合用这个的，一些游戏类的素材比较多的应用，打开网页时，预先加载需要用到的各种资源如图片、flash以及各种静态文件。所有的都加载完后，我们再进行页面的初始化。</p>
        <hr>
        <p>all方法的效果实际上是「谁跑的慢，以谁为准执行回调」，那么相对的就有另一个方法「谁跑的快，以谁为准执行回调」，这就是race方法，这个词本来就是赛跑的意思。race的用法与all一样，我们把上面runAsync1的延时改为1秒来看一下</p>
        <script type="text/xianjs" ui-bs>
            Promise
                .race([runAsync1(), runAsync2(), runAsync3()])
                .then(function(results){ console.log(results); });
        </script>
        <p>这三个异步操作同样是并行执行的。结果你应该可以猜到，1秒后runAsync1已经执行完了，此时then里面的就执行了。</p>
        <p>你猜对了吗？不完全，是吧。在then里面的回调开始执行时，runAsync2()和runAsync3()并没有停止，仍旧再执行。于是再过1秒后，输出了他们结束的标志。</p>
        <p>这个race有什么用呢？使用场景还是很多的，比如我们可以用race给某个异步请求设置超时时间，并且在超时后执行相应的操作</p>
        <script type="text/javascript" ui-bs>
            //真实测试一下
            //请求某个图片资源
            function requestImg(){
                return new Promise(function(resolve, reject){
                    var img = new Image();
                    img.onload = function(){ resolve(img); }
                    img.src = 'xxxxxx';
                });
            }
            //延时函数，用于给请求计时
            function timeout(){
                return new Promise(function(resolve, reject){
                    setTimeout(function(){ reject('图片请求超时'); }, 5000);
                });
            }
            Promise
            .race([requestImg(), timeout()])
            .then(function(results){ alert("正常结果 || "+results); })
            .catch(function(reason){ alert("出错了 || "+reason); });
        </script>
        <p>requestImg函数会异步请求一张图片，我把地址写为"xxxxxx"，所以肯定是无法成功请求到的。timeout函数是一个延时5秒的异步操作。我们把这两个返回Promise对象的函数放进race，于是他俩就会赛跑，如果5秒之内图片请求成功了，那么遍进入then方法，执行正常的流程。如果5秒钟图片还未成功返回，那么timeout就跑赢了，则进入catch，报出“图片请求超时”的信息。</p>
    </article>
</div>
